﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace Light.Utility
{
    /// <summary>
    /// DataTable与实体互相转换的工具类
    /// </summary>
    public class ImportHelper
    {

        /// <summary>
        /// 将DataTable转换成 T 类型对象的集合（列名与特性值完全对应）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> ConvertTo<T>(DataTable dt) where T : class, new()
        {
            return ConvertTo<T>(dt, null);
        }

        /// <summary>
        /// 将DataTable转换成 T 类型对象的集合（列名与特性值不完全对应）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dicMapping">列名映射（由于一个属性可能在不同的单据中ImportField值可以不同，如：房号* =>房号）</param>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> ConvertTo<T>(DataTable dt, Dictionary<string, string> dicMapping) where T : class, new()
        {
            string importClass = ReflectionHelper.GetImportField<T>();
            if (String.IsNullOrEmpty(importClass))
            {
                throw new NotSupportedException("类型" + typeof(T).Name + "不支持导入！如需支持，请添加带Name参数的 ImportField 特性标签");
            }

            List<T> models = new List<T>();

            Dictionary<string, string> importFields = ReflectionHelper.GetImportFields<T>(false);

            foreach (var key in importFields.Keys)
            {
                string columnName = GetRealColumnName(dicMapping, importFields[key]);
                if (columnName != null && columnName.Contains("*") && !dt.Columns.Contains(columnName))
                {
                    throw new Exception("导入的表格[ " + dt.TableName + " ] 不包含必填列[ " + columnName + " ]。");
                }
            }


            int colCount = dt.Columns.Count;
            int rowCount = dt.Rows.Count;

            Dictionary<string, string> dic = new Dictionary<string, string>();//用于保存列名映射，减少遍历次数

            for (int i = 0; i < rowCount; i++)
            {
                DataRow row = dt.Rows[i];

                if (DataTableHelper.IsEmptyRow(row))
                {
                    continue;
                }
                T model = new T();
                foreach (var pName in importFields.Keys)
                {
                    string oldColumnName = importFields[pName];
                    string newColumnName = string.Empty;

                    if (dic.Keys.Contains(pName))
                        newColumnName = dic[pName];
                    else
                        newColumnName = GetRealColumnName(dicMapping, oldColumnName);//从自定义的列名集合中获取新列名

                    object value = null;

                    if (dt.Columns.Contains(newColumnName))      //优先匹配自定义的列名
                    {
                        value = row[newColumnName];
                    }
                    else if (dt.Columns.Contains(oldColumnName)) //表格中找不到匹配的列名，则用默认列名
                    {
                        value = row[oldColumnName];
                        newColumnName = oldColumnName;
                    }
                    else
                    {
                        continue;
                    }

                    if (!dic.Keys.Contains(pName))
                        dic.Add(pName, newColumnName);

                    if (newColumnName.Contains("*") && (value == null || value == System.DBNull.Value || String.IsNullOrEmpty(value.ToString())))
                    {
                        throw new Exception("第[ " + (i + 1) + " ]行，必填项[ " + newColumnName + " ] 不能为空。");
                    }
                    else if (value != null && value != System.DBNull.Value && !String.IsNullOrEmpty(value.ToString()))
                    {
                        ReflectionHelper.SetPropertyValue(model, pName, value);
                    }
                }
                models.Add(model);
            }

            return models;
        }


        /// <summary>
        /// 检查导入的表的列是否有必填列
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <param name="dicMapping"></param>
        public static void CheckRequiredCol<T>(DataTable dt, Dictionary<string, string> dicMapping) where T : class, new()
        {
            string importClass = ReflectionHelper.GetImportField<T>();
            if (String.IsNullOrEmpty(importClass))
            {
                throw new NotSupportedException("类型" + typeof(T).Name + "不支持导入！如需支持，请添加带Name参数的 ImportField 特性标签");
            }

            List<T> models = new List<T>();

            Dictionary<string, string> importFields = ReflectionHelper.GetImportFields<T>(false);

            foreach (var key in importFields.Keys)
            {
                string columnName = GetRealColumnName(dicMapping, importFields[key]);
                if (columnName != null && columnName.Contains("*") && !dt.Columns.Contains(columnName))
                {
                    throw new Exception("导入的表格[ " + dt.TableName + " ] 不包含必填列[ " + columnName + " ]。");
                }
            }
        }


        /// <summary>
        /// 表行转化成实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="row"></param>
        /// <param name="dicMapping"></param>
        /// <returns></returns>
        public static T ConvertTo<T>(DataRow row, Dictionary<string, string> dicMapping) where T : class, new()
        {
            Dictionary<string, string> importFields = ReflectionHelper.GetImportFields<T>(false);
            Dictionary<string, string> dic = new Dictionary<string, string>();//用于保存列名映射，减少遍历次数
            if (DataTableHelper.IsEmptyRow(row))
            {
                return null;
            }
            T model = new T();
            foreach (var pName in importFields.Keys)
            {
                string oldColumnName = importFields[pName];
                string newColumnName = string.Empty;

                if (dic.Keys.Contains(pName))
                    newColumnName = dic[pName];
                else
                    newColumnName = GetRealColumnName(dicMapping, oldColumnName);//从自定义的列名集合中获取新列名

                object value = null;

                if (row.Table.Columns.Contains(newColumnName))      //优先匹配自定义的列名
                {
                    value = row[newColumnName];
                }
                else if (row.Table.Columns.Contains(oldColumnName)) //表格中找不到匹配的列名，则用默认列名
                {
                    value = row[oldColumnName];
                    newColumnName = oldColumnName;
                }
                else
                {
                    continue;
                }

                if (!dic.Keys.Contains(pName))
                    dic.Add(pName, newColumnName);

                if (newColumnName.Contains("*") && (value == null || value == System.DBNull.Value || String.IsNullOrEmpty(value.ToString())))
                {
                    throw new Exception("必填项[ " + newColumnName + " ] 不能为空。");
                }

                else if (value != null && value != System.DBNull.Value && !String.IsNullOrEmpty(value.ToString()))
                {
                    ReflectionHelper.SetPropertyValue(model, pName, value);
                }
            }

            return model;
        }

        /// <summary>
        /// 将实体中标识为可导出的属性转化成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(T[] models)
        {
            return ToDataTable<T>(models, null, null);
        }

        /// <summary>
        /// 将实体中标识为可导出的属性转化成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <param name="excepts">不需要导出的属性集合</param>
        public static DataTable ToDataTable<T>(T[] models, List<string> excepts)
        {
            return ToDataTable<T>(models, excepts, null);
        }

        /// <summary>
        /// 将实体中标识为可导出的属性转化成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <param name="excepts">不需要导出的属性集合</param>
        /// <param name="dicMapping">需要重命名的，《属性名，新列名》 字典</param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(T[] models, List<string> excepts, Dictionary<string, string> dicMapping)
        {
            string importClass = ReflectionHelper.GetImportField<T>();
            if (String.IsNullOrEmpty(importClass))
            {
                throw new NotSupportedException("类型" + typeof(T).Name + "不支持导出！如需支持，请添加带Name参数的ImportField 特性标签");
            }
            string tableName = ReflectionHelper.GetImportField<T>() + "_" + DateTime.Now.ToString("yyyyMMdd_hhmmss");
            DataTable dt = new DataTable(tableName);


            Dictionary<string, string> fields = ReflectionHelper.GetImportFields<T>(true);
            foreach (var pName in fields.Keys)
            {
                if (excepts != null && excepts.Contains(pName))
                    continue;

                if (!dt.Columns.Contains(fields[pName]))
                {

                    string columnName = fields[pName];

                    //重命名 列名，如原特性上写的是：“管理费” 改为自定义的 “物业费”
                    if (dicMapping != null && dicMapping.Keys.Contains(pName))
                        columnName = dicMapping[pName];

                    DataColumn column = new DataColumn(columnName);
                    if (models != null && models.Length > 0)
                    {
                        object value = ReflectionHelper.GetPropertyValue<T>(models[0], pName);
                        if (value is String || value is int || value is Double)
                        {
                            column.DataType = value.GetType();
                        }
                        else if (value is DateTime)
                        {
                            column.DataType = typeof(String);
                        }
                        else if (value is Decimal)
                        {
                            column.DataType = typeof(Double);
                        }
                    }
                    dt.Columns.Add(column);

                }
            }
            foreach (T model in models)
            {
                DataRow row = dt.NewRow();

                foreach (var pName in fields.Keys)
                {
                    if (excepts != null && excepts.Contains(pName))
                        continue;

                    object value = ReflectionHelper.GetPropertyValue<T>(model, pName);

                    string columnName = fields[pName];

                    //重命名 列名，如原特性上写的是：“管理费” 改为自定义的 “物业费”
                    if (dicMapping != null && dicMapping.Keys.Contains(pName))
                        columnName = dicMapping[pName];

                    if (value is Enum)
                    {
                        row[columnName] = EnumHelper.GetEnumDesc((Enum)value);
                    }
                    else if (value is DateTime)
                    {
                        row[columnName] = Convert.ToDateTime(value).ToString("yyyy-MM-dd");
                    }
                    else if (value is Decimal)
                    {
                        row[columnName] = Convert.ToDecimal(value).ToString("F2");
                    }
                    else
                    {
                        try
                        {
                            row[columnName] = value;
                        }
                        catch (Exception ex)
                        {
                            string path = WebHelper.CurContext.Server.MapPath("~/reflectionHelper.error");
                            TextHelper.AppendString(path, string.Format("========DataTableHelper.ToDataTable 发生错误：{0} 时间：{1}", ex.Message, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));

                            continue;
                        }
                    }
                }

                dt.Rows.Add(row);
            }
            return dt;
        }

        /// <summary>
        /// 将实体中标识为可导出的属性转化成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <returns></returns>
        public static DataTable ToDisplayTable<T>(params T[] models)
        {
            string displayName = ReflectionHelper.GetDisplayName(typeof(T), typeof(T).Name);
            string tableName = displayName + "_" + DateTime.Now.ToString("yyyyMMdd_hhmmss");

            DataTable dt = new DataTable(tableName);

            Dictionary<string, string> names = ReflectionHelper.GetDisplayNames<T>();
            foreach (var pName in names.Keys)
            {
                dt.Columns.Add(names[pName]);
            }
            foreach (T model in models)
            {
                DataRow row = dt.NewRow();

                foreach (var pName in names.Keys)
                {
                    object value = ReflectionHelper.GetPropertyValue<T>(model, pName);
                    if (value is Enum)
                    {
                        row[names[pName]] = EnumHelper.GetEnumDesc((Enum)value);
                    }
                    else if (value is DateTime)
                    {
                        row[names[pName]] = Convert.ToDateTime(value).ToString("yyyy-MM-dd");
                    }
                    else if (value is Decimal)
                    {
                        row[names[pName]] = Convert.ToDecimal(value).ToString("F2");
                    }
                    else if (value is Boolean)
                    {
                        row[names[pName]] = ((bool)value) ? "√" : " ";
                    }
                    else
                    {
                        row[names[pName]] = value;
                    }
                }

                dt.Rows.Add(row);
            }
            return dt;
        }


        /// <summary>
        /// DataTable是否包含指定列集合
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public static bool Contains(DataTable dt, params string[] columns)
        {
            bool has = true;
            foreach (var column in columns)
            {
                if (!dt.Columns.Contains(column))
                {
                    has = false;
                    break;
                }
            }
            return has;
        }

        static string GetRealColumnName(Dictionary<string, string> dicMapping, string oldColumnName)
        {
            if (oldColumnName != null && dicMapping != null && dicMapping.ContainsKey(oldColumnName))//转换列名
            {
                oldColumnName = dicMapping[oldColumnName];
            }
            return oldColumnName;
        }
    }
}
